package com.longyou.comm.conntroller;


import static com.unknow.first.mfa.config.MfaFilterConfig.__MFA_TOKEN_USER_CACHE_KEY;
import static com.unknow.first.mfa.config.MfaFilterConfig.__MFA_TOKEN_USER_GOOGLE_SECRET_CACHE_KEY;
import static org.cloud.constant.CoreConstant.TEL_VALIDATE_CODE_CACHE_KEY;
import static org.cloud.constant.MfaConstant.CORRELATION_YOUR_GOOGLE_KEY;
import static org.cloud.constant.MfaConstant._GOOGLE_MFA_USER_SECRET_REF_ATTR_NAME;
import static org.cloud.constant.MfaConstant._GOOGLE_MFA_USER_SECRET_REF_FlAG_ATTR_NAME;

import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.longyou.comm.service.FrameUserRefService;
import com.unknow.first.annotation.MfaAuth;
import com.unknow.first.util.GoogleAuthenticatorUtil;
import com.unkow.first.telegram.constant.TelConstant.TelMessageOperate;
import com.unkow.first.telegram.dto.TelValidateCodeResultDTO;
import com.unkow.first.telegram.util.TelMsgSendUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.cloud.constant.CoreConstant;
import org.cloud.constant.CoreConstant.AuthMethod;
import org.cloud.constant.CoreConstant.OperateLogType;
import org.cloud.context.RequestContext;
import org.cloud.context.RequestContextManager;
import org.cloud.core.redis.RedisUtil;
import org.cloud.dimension.annotation.SystemResource;
import org.cloud.entity.LoginUserDetails;
import org.cloud.exception.BusinessException;
import org.cloud.feign.utils.FeignUtil;
import org.cloud.logs.annotation.AuthLog;
import org.cloud.utils.AES128Util;
import org.cloud.utils.IPUtil;
import org.cloud.vo.CommonApiResult;
import org.cloud.vo.FrameUserRefVO;
import org.cloud.vo.ResponseResult;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/user/mfa")
@SystemResource(path = "/common/user/mfa")
@Api(value = "UserMfaController", tags = "双因子验证API")
@Slf4j
public class UserMfaController {

    @Autowired
    private FrameUserRefService frameUserRefService;
    @Value("${system.mfa.expired-time:1800}")
    private Long expiredTime;

    /**
     * 校验谷歌验证码是否已经绑定
     *
     * @return
     * @throws Exception
     */
    @ApiOperation("校验谷歌验证码是否已经绑定")
    @GetMapping("/checkUserGoogleSecretBindStatus")
    @SystemResource
//    @AuthLog(bizType = "framework", desc = "校验谷歌验证码是否已经绑定", operateLogType = OperateLogType.LOG_TYPE_FRONTEND)
    public CommonApiResult<Map<String, Object>> checkUserGoogleSecretBindStatus() throws Exception {
        LoginUserDetails loginUserDetails = RequestContextManager.single().getRequestContext().getUser();
        CommonApiResult<Map<String, Object>> responseResult = CommonApiResult.createSuccessResult();
        FrameUserRefVO frameUserRefVO = frameUserRefService.getUserRefByAttributeName(loginUserDetails.getId(),
            _GOOGLE_MFA_USER_SECRET_REF_FlAG_ATTR_NAME.value());
        final Map<String, Object> returnData = new LinkedHashMap<>();
        if (frameUserRefVO != null && "true".equals(frameUserRefVO.getAttributeValue())) {
            returnData.put(_GOOGLE_MFA_USER_SECRET_REF_FlAG_ATTR_NAME.value(), true);
        } else {
            String googleSecretEnc = GoogleAuthenticatorUtil.single().getCurrentUserVerifyKey(true);
            if (googleSecretEnc == null) {
                frameUserRefVO = GoogleAuthenticatorUtil.single().createNewUserRefVO(loginUserDetails);
                frameUserRefService.create(frameUserRefVO);
                googleSecretEnc = frameUserRefVO.getAttributeValue();
            }
            final String googleSecret = AES128Util.single().decrypt(googleSecretEnc);
            returnData.put(_GOOGLE_MFA_USER_SECRET_REF_FlAG_ATTR_NAME.value(), false);
            returnData.put("description", CORRELATION_YOUR_GOOGLE_KEY.description());
            returnData.put("secret", googleSecret);
            returnData.put("secretQRBarcode", GoogleAuthenticatorUtil.single().getQRBarcode(loginUserDetails.getUsername(), googleSecret));
//            returnData.put("secretQRBarcodeURL",
//                GoogleAuthenticatorUtil.single().getQRBarcodeURL(loginUserDetails.getUsername(), "", googleSecret));
            RedisUtil.single().set(__MFA_TOKEN_USER_GOOGLE_SECRET_CACHE_KEY + loginUserDetails.getId(), googleSecretEnc);
        }
        responseResult.setData(returnData);
        return responseResult;
    }

    /**
     * 绑定谷歌验证码
     *
     * @return
     * @throws Exception
     */
    @ApiOperation("绑定谷歌验证")
    @GetMapping("/bindUserGoogleSecret")
    @SystemResource(value = "bindUserGoogleSecret", description = "绑定谷歌验证")
    @AuthLog(bizType = "framework", desc = "绑定谷歌验证", operateLogType = OperateLogType.LOG_TYPE_FRONTEND)
    public CommonApiResult<FrameUserRefVO> bindUserGoogleSecret() throws Exception {
        // 绑定的时候每次都要重新的生成一个出来
        String googleSecret = GoogleAuthenticatorUtil.single().getCurrentUserVerifyKey();
        if (!GoogleAuthenticatorUtil.single().checkGoogleVerifyCode(googleSecret)) {
            throw new BusinessException("system.error.google.valid", 400);
        }
        LoginUserDetails loginUserDetails = RequestContextManager.single().getRequestContext().getUser();
        CommonApiResult<FrameUserRefVO> responseResult = CommonApiResult.createSuccessResult();
        FrameUserRefVO frameUserRefVO = frameUserRefService.getUserRefByAttributeName(loginUserDetails.getId(),
            _GOOGLE_MFA_USER_SECRET_REF_FlAG_ATTR_NAME.value());
        if (frameUserRefVO == null) {
            frameUserRefVO = new FrameUserRefVO();
            frameUserRefVO.setUserId(loginUserDetails.getId());
            frameUserRefVO.setAttributeName(_GOOGLE_MFA_USER_SECRET_REF_FlAG_ATTR_NAME.value());
            frameUserRefVO.setAttributeValue("true");
            frameUserRefVO.setRemark(_GOOGLE_MFA_USER_SECRET_REF_FlAG_ATTR_NAME.description());
            frameUserRefService.create(frameUserRefVO);
        } else {
            frameUserRefVO.setAttributeValue("true");
            frameUserRefService.update(frameUserRefVO);
        }
        responseResult.setData(frameUserRefVO);
        ThreadUtil.execAsync(() -> RedisUtil.single().removeUserLoginToken(loginUserDetails.getId()));
        return responseResult;
    }

    /**
     * 校验谷歌验证码绑定状态
     *
     * @return
     * @throws Exception
     */
    @AuthLog(bizType = "framework", desc = "校验当前用户的谷歌验证码", operateLogType = OperateLogType.LOG_TYPE_FRONTEND)
    @ApiOperation("校验当前用户的谷歌验证码")
    @GetMapping("/checkCurrentUserGoogleCode/{mfaValue}")
    @SystemResource(value = "checkCurrentUserGoogleCode", description = "校验当前用户的谷歌验证码", authMethod = CoreConstant.AuthMethod.ALLSYSTEMUSER)
    public CommonApiResult<Boolean> checkCurrentUserGoogleCode(@PathVariable String mfaValue, HttpServletRequest httpServletRequest)
        throws Exception {
        GoogleAuthenticatorUtil.single().verifyCurrentUserBindGoogleKey();
        RequestContext currentRequestContext = RequestContextManager.single().getRequestContext();
        LoginUserDetails user = currentRequestContext.getUser();
        String googleSecret = GoogleAuthenticatorUtil.single().getCurrentUserVerifyKey();
        final Boolean isValidatePass = GoogleAuthenticatorUtil.single().checkGoogleVerifyCode(googleSecret, mfaValue);
        String ipHash = RedisUtil.single().getMd5Key(IPUtil.single().getIpAddress(httpServletRequest));
        RedisUtil.single().set(__MFA_TOKEN_USER_CACHE_KEY + user.getId() + ":" + ipHash, isValidatePass, expiredTime);
        return CommonApiResult.createSuccessResult(isValidatePass);
    }

    /**
     * 重置谷歌验证码绑定状态
     *
     * @return
     * @throws Exception
     */
    @AuthLog(bizType = "framework", desc = "管理员：重置谷歌验证码状态", operateLogType = OperateLogType.LOG_TYPE_BACKEND)
    @ApiOperation("管理员：重置谷歌验证码状态")
    @GetMapping("/resetBindUserGoogleSecretFlag/{userId}")
    @SystemResource(value = "resetBindUserGoogleSecretFlag", description = "重置谷歌验证码状态", authMethod = AuthMethod.BYUSERPERMISSION)
    public ResponseResult resetBindUserGoogleFlag(@PathVariable("userId") Long userId) throws Exception {
        return resetUserGoogle(userId);
    }

    /**
     * 重置谷歌验证码绑定状态
     *
     * @return
     * @throws Exception
     */
    @AuthLog(bizType = "framework", desc = "管理员：重置自己的谷歌验证码", operateLogType = OperateLogType.LOG_TYPE_FRONTEND)
    @ApiOperation("重置自己的谷歌验证码")
    @GetMapping("/resetMyGoogleSecretFlag")
    @SystemResource(value = "resetMyGoogleSecretFlag", description = "重置自己的谷歌验证码", authMethod = AuthMethod.ALLSYSTEMUSER)
    @MfaAuth
    public ResponseResult resetMyGoogleSecretFlag() throws Exception {
        return resetUserGoogle(RequestContextManager.single().getRequestContext().getUser().getId());
    }

    @NotNull
    private ResponseResult resetUserGoogle(Long userId) {
        ResponseResult responseResult = ResponseResult.createSuccessResult();
        FrameUserRefVO frameUserRefVO = frameUserRefService.getUserRefByAttributeName(userId,
            _GOOGLE_MFA_USER_SECRET_REF_FlAG_ATTR_NAME.value());
        if (frameUserRefVO != null) {
            frameUserRefService.delete(frameUserRefVO.getId());
        }
        frameUserRefVO = frameUserRefService.getUserRefByAttributeName(userId, _GOOGLE_MFA_USER_SECRET_REF_ATTR_NAME.value());
        if (frameUserRefVO != null) {
            frameUserRefService.delete(frameUserRefVO.getId());
        }
        RedisTemplate redisTemplate = RedisUtil.single().getRedisTemplate();
        Set<?> keys = redisTemplate.keys(__MFA_TOKEN_USER_CACHE_KEY + userId + ":*");
        if (CollectionUtils.isNotEmpty(keys)) {
            redisTemplate.delete(keys);
        }
        ThreadUtil.execAsync(() -> RedisUtil.single().removeUserLoginToken(userId));
        return responseResult;
    }

//    @ApiOperation("验证校验谷歌的测试API")
//    @GetMapping("/checkAspectGoogleMfa")
//    @MfaAuth
//    @SystemResource(value = "checkAspectGoogleMfa", description = "验证校验谷歌的测试API", authMethod = AuthMethod.ALLSYSTEMUSER)
//    public CommonApiResult<Boolean> checkAspectGoogleMfa() throws Exception {
//        return CommonApiResult.createSuccessResult(true);
//    }

    @Value("${system.telegram.bot.config.IS_OPEN_VALIDATE:false}")
    private Boolean isOpenValidate;

    @ApiOperation("是否开放tel验证码")
    @GetMapping("/isOpenTelegramValidateCode")
    @SystemResource(authMethod = AuthMethod.NOAUTH)
    public CommonApiResult<Boolean> isOpenTelegramValidateCode() throws Exception {
        return CommonApiResult.createSuccessResult(isOpenValidate);
    }

    @Value("${system.show_verify_code:false}")
    private Boolean isShowVerifyCode;  // 方便通过api登录，这里增加一个校验

    @ApiOperation("获取telegram验证码")
    @GetMapping("/getTelegramValidateCode")
    @SystemResource(authMethod = AuthMethod.NOAUTH)
    public CommonApiResult<TelValidateCodeResultDTO> getTelegramValidateCode(@RequestParam(required = false) String userName,
        TelMessageOperate operate, HttpServletRequest request) throws Exception {

        TelValidateCodeResultDTO codeResultDTO = TelValidateCodeResultDTO.builder()
            .validateCodeKey(RandomUtil.randomString(32))
            .validateValue(RandomUtil.randomString(6))
            .operate(operate)
            .build();
        String msg = "正在进行*** %s ***操作验证码是：***`%s`*** ，请不要告诉任何人，验证码在5分钟内有效";
        if (operate.isMustLogin) {
            LoginUserDetails loginUser = FeignUtil.single().getLoginUser(request.getHeader("Authorization"));
            if (loginUser == null) {
                throw new BusinessException("pls.login", HttpStatus.BAD_REQUEST);
            }
            codeResultDTO.setUserId(loginUser.getId());
            msg = "用户" + loginUser.getUsername() + msg;
        } else if (operate.equals(TelMessageOperate.LOGIN)) {
            Assert.isTrue(StrUtil.isNotEmpty(userName), "login.error.userName.is.null");
            msg = "用户" + userName + "," + msg;
        }
        RedisUtil.single().set(String.format(TEL_VALIDATE_CODE_CACHE_KEY, codeResultDTO.getValidateCodeKey()), codeResultDTO, 300L);

        Assert.isTrue(
            TelMsgSendUtil.single().sendMessageMarkdown(String.format(msg, codeResultDTO.getOperate().desc, codeResultDTO.getValidateValue()))
                == 200, "TEL验证码发送失败，请重试");
        if (!isShowVerifyCode) {
            codeResultDTO.setValidateValue(null);
        }
        return CommonApiResult.createSuccessResult(codeResultDTO);
    }


}
